package com.reebake.ideal.security.config;

import com.reebake.ideal.security.access.DefaultBearerTokenResolver;
import com.reebake.ideal.security.access.BearerTokenAuthenticationFilter;
import com.reebake.ideal.security.core.GenericCredentialResolver;
import com.reebake.ideal.security.properties.ClientSecurityProperties;
import com.reebake.ideal.security.service.SecurityMetadataSourceService;
import com.reebake.ideal.security.web.ResourceAccessDeniedHandler;
import com.reebake.ideal.security.web.ResourceAuthenticationEntryPoint;
import com.reebake.ideal.security.web.ResourceAuthenticationFailureHandler;
import com.reebake.ideal.security.web.SmartAuthorizationManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@Slf4j
public class ClientAuthenticationFilterConfig {
	
	@Autowired
	private AuthenticationManager authenticationManager;
	@Autowired
	private ClientSecurityProperties clientSecurityProperties;
    @Autowired(required = false)
    private SecurityMetadataSourceService securityMetadataSourceService;

    @Bean
	SecurityFilterChain authenticationSecurityFilterChain(HttpSecurity http, AuthenticationEntryPoint authenticationEntryPoint) throws Exception {
		log.info("启动SecurityFilterChain: global client authentication");
		http.authorizeHttpRequests((authorize) -> authorize
	            .anyRequest().access(authorizationManager())
	        )
	        .exceptionHandling(
					exceptionHandling -> exceptionHandling.authenticationEntryPoint(authenticationEntryPoint)
							.accessDeniedHandler(accessDeniedHandler())
	        );
	    
	    http.csrf(csrf -> csrf.disable()).formLogin(formLogin -> formLogin.disable());
        http.addFilterBefore(genericAuthenticationFilter(authenticationEntryPoint), UsernamePasswordAuthenticationFilter.class);

        http.cors(Customizer.withDefaults());

        return http.build();
	}

    BearerTokenAuthenticationFilter genericAuthenticationFilter(AuthenticationEntryPoint authenticationEntryPoint) throws Exception {
        BearerTokenAuthenticationFilter bearerTokenAuthenticationFilter = new BearerTokenAuthenticationFilter(authenticationManager);
        bearerTokenAuthenticationFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
        bearerTokenAuthenticationFilter.setCredentialResolver(bearerTokenResolver());
        bearerTokenAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler(authenticationEntryPoint));

        return bearerTokenAuthenticationFilter;
	}
	
	public AuthenticationFailureHandler authenticationFailureHandler(AuthenticationEntryPoint authenticationEntryPoint) {
		return new ResourceAuthenticationFailureHandler(authenticationEntryPoint);
	}
	
	@Bean
	AuthenticationEntryPoint resourceAuthenticationEntryPoint() {
		return new ResourceAuthenticationEntryPoint(clientSecurityProperties.getEntryPointUrl());
	}

	public AccessDeniedHandler accessDeniedHandler() {
		return new ResourceAccessDeniedHandler();
	}

    GenericCredentialResolver bearerTokenResolver() {
		return new DefaultBearerTokenResolver();
	}
	
	SmartAuthorizationManager authorizationManager() {
        SmartAuthorizationManager authorizationManager = new SmartAuthorizationManager();
        authorizationManager.setSecurityMetadataSourceService(securityMetadataSourceService);
        return authorizationManager;
	}
}
